package cn.tedu.tmall.basic.service.impl;

import cn.tedu.tmall.basic.dao.persist.repository.IDistrictRepository;
import cn.tedu.tmall.basic.dao.repository.IDistrictCacheRepository;
import cn.tedu.tmall.basic.pojo.po.DistrictPO;
import cn.tedu.tmall.basic.service.IDistrictService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;

@Slf4j
@Service("DistrictServiceImpl")
public class DistrictServiceImpl implements IDistrictService {
    @Autowired
    private IDistrictRepository districtRepository;
    @Autowired
    private IDistrictCacheRepository districtCacheRepository;

    public DistrictServiceImpl() {
        log.debug("创建业务类对象：DistrictServiceImpl");
    }

    @Override
    public List<DistrictPO> listByParent(Long parentId) {
        log.debug("开始执行【根据父级查询子级地区列表】的业务，参数：{}", parentId);
        //return districtRepository.listByParent(parentId);
        return districtCacheRepository.listByParent(parentId);
    }

    @Override
    public void initDict() {
        //1 从数据库查询
        //  1 怎么查? select * FROM table 全查出来,往redis 里一塞
        // 我们要考虑怎么使用我们省市区数据
        // 先选择省,再选择市 , 再选择区 redis用什么数据结构 list

        //1 删除的逻辑写到这里么?
        // 从代码逻辑上任何毛病都没有 但是我们要保证系统的健壮性,如果数据库的数据因为某种原因没了
        //我们直接把redis的数据也个删了,导致用户查询不到省市区数据,我们是电商,新用户下不单.
        //我们在删除之前应该判断下,如果数据库没有,我们先不删redis,让用户去用redis数据.
        //districtCacheRepository.deleteAll();

        //Long parentId = 0L;
        //目前我们只处理的pId 是 0 最顶层的,
        // 下面的该如何处理?
        //1 递归 自己调用自己
        //2 for(int i = 0 ,i<5000;i++) i当做parentId
        //3 还有一种方案,省市区的数据不会很多,直接把select *  全查,放到tomcat内存里问题不大,
        // 然后根据parentId
        //Map<Long,List<>> map  long parentId list 省市区数据. 实现的小算法
        //遍历map 存入redis
        // parent_id xxx数据
        //   1        北京
        //   1        天津
        //   2        东城区
        //map<1,List<2> 北京,天津>
        //map<2,List<1> 东城区>
        //方案4 我先从数据库把所有的parentId 全查出来,放到一个set,然后遍历这个set
        //基于set里的parentId 循环查
        //优点 简单 基于现有存在的逻辑,
        //缺点 多次查询数据库
        //禁忌 for循环里 查数据库  数据库连接是珍贵的资源
        //list parentIds =  select parent_Id from dict_district;
        //parentIds.for(
        //      List<DistrictPO> districtPOS = districtRepository.listByParent(parentId);
        // )

        //List<DistrictPO> districtPOS = districtRepository.listByParent(parentId);
        //if (CollectionUtils.isEmpty(districtPOS)) {
        //log.error("查询省市区信息为空,入参:{},出参:{}",parentId,districtPOS);
        //报警... 通知dba,op,通知领导.(待商榷)
        //} else {
        //如果不为空我们再删redis的数据,把从数据库查询到的最新的数据放入到redis

        //2 删除的逻辑写到这里么? 如果redis删失败了,大概率redis出问题了,其实系统都应该有监控
        //包括环境监控 数据库,redis 服务器 ,业务监控 程序业务的行为监控
        //districtCacheRepository.deleteAll();

        //把parentI的0 ,和 districtPOS 存到redis 里 用list结构
        //districtCacheRepository.saveDict(parentId,districtPOS);

        //基本可用,但是有个bug 重启多次后,数据会一直往redis里面搞 乱套了
        //怎么办?
        // 1 加个判断 key存在了,就不加了 行不行
        // 行  如果数据库的省市区更新了,并且你的redis没有设置过期时间,你的数据有可能永远没机会更新了.
        // 不行
        // 2 先删 再加hj
        //}
        queryAllParentIds();
        //queryAll();
    }

    /**
     * 递归方案
     */

    /**
     * 优点 占用内存相对少
     * 缺点 多次查询数据库
     */
    private void queryAllParentIds(){
        //1先查所有的parentId 并且去重
        List<Long> all = districtRepository.listAllParentIds();
        if (CollectionUtils.isEmpty(all)){
            log.error("查询省市区信息为空");
            //报警... 通知dba,op,通知领导.(待商榷)
        }else {
            //删除redis
            districtCacheRepository.deleteAll();
            all.forEach(parentId->{
                //循环去重的parentIds,通过parentId查询当前parentId下的省市区
                List<DistrictPO> districtPOS =
                        districtRepository.listByParent(parentId);
                //保存到redis
                districtCacheRepository.saveDict(parentId,districtPOS);
            });
        }
    }

    /**
     * 优点 一次查询数据库
     * 缺点 会占内存 ,我们把所有的数据全查出来
     */
    private void queryAll(){
        //1 查询所有的数据
        List<DistrictPO> all = districtRepository.listAll();
        if (CollectionUtils.isEmpty(all)){
            log.error("查询省市区信息为空");
            //报警... 通知dba,op,通知领导.(待商榷)
        }else {
            //2 list转换成map ,按照parentId 进行分组
            Map<Long,List<DistrictPO>> result = groupByParent(all);
            //删除redis
            districtCacheRepository.deleteAll();

            //把parentI的0 ,和 districtPOS 存到redis 里 用list结构
            result.forEach((parentId,districtPOS)->
                    districtCacheRepository.saveDict(parentId,districtPOS));
        }
    }

    private Map<Long,List<DistrictPO>> groupByParent(List<DistrictPO> districtPOS){
        Map<Long,List<DistrictPO>> result = new HashMap<>();
        for (DistrictPO po : districtPOS) {
            Long parentId = po.getParentId();
            //先判断这个pId 是否在map里存在
            if(result.containsKey(parentId)) {
                List<DistrictPO> existList = result.get(parentId);
                existList.add(po);
            }else {
                //第一次 肯定map 是空的
                List<DistrictPO> temp = new ArrayList<>();
                temp.add(po);
                //size 是不是1
                result.put(parentId,temp);
                //map<p_id=0,List<index=0>> 外面那个大list的第一条数据
            }
        }
        return result;
    }




}
